use std::fmt;

use from_pest::FromPest;
use pest::iterators;
use pest_ast::FromPest;
use tracing::{debug, trace};

use crate::ast_define::function::FuncRParams;
use crate::ast_define::general::PrimaryExp;
use crate::ast_define::literal::Ident;
use crate::ast_define::operator::*;
use crate::ast_define::{MetaInfo, Rule, get_meta_info};

// * 加减表达式 AddExp -> MulExp | AddExp ('+' | '-') MulExp
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::AddExpRecursiveMove))]
pub struct AddExpRecursiveMove {
    pub linear_operator: LinearOperator,
    pub mul_exp: Box<MulExp>,
    pub add_exp_recursive_move: Box<Option<AddExpRecursiveMove>>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for AddExpRecursiveMove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.add_exp_recursive_move.as_ref() {
            Some(this) => write!(f, "{} {} {}", self.linear_operator, self.mul_exp, this),
            None => write!(f, "{} {}", self.linear_operator, self.mul_exp),
        }
    }
}

#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::LinearOperator_MulExp))]
pub struct LinearOperator_MulExp {
    pub linear_operator: LinearOperator,
    pub mul_exp: Box<MulExp>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for LinearOperator_MulExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}", self.linear_operator, self.mul_exp)
    }
}

#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::MulExp_AddExpRecursiveMove))]
pub struct MulExp_AddExpRecursiveMove {
    pub mul_exp: Box<MulExp>,
    pub add_exp_recursive_move: Option<AddExpRecursiveMove>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for MulExp_AddExpRecursiveMove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.add_exp_recursive_move.as_ref() {
            Some(this) => write!(f, "{} {}", self.mul_exp, this),
            None => write!(f, "{}", self.mul_exp),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum AddExp {
    LinearOperator_MulExp(LinearOperator_MulExp),
    MulExp_AddExpRecursiveMove(MulExp_AddExpRecursiveMove),
}

impl fmt::Display for AddExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AddExp::LinearOperator_MulExp(this) => write!(f, "{this}"),
            AddExp::MulExp_AddExpRecursiveMove(this) => write!(f, "{this}"),
        }
    }
}

impl<'pest> FromPest<'pest> for AddExp {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 AddExp");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in AddExp:\n{pair:#?}");
        let Rule::AddExp = pair.as_rule() else {
            trace!("\n{:#?} != Rule::AddExp", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::LinearOperator_MulExp => Ok(AddExp::LinearOperator_MulExp(
                LinearOperator_MulExp::from_pest(&mut inner)?,
            )),
            Rule::MulExp_AddExpRecursiveMove => Ok(AddExp::MulExp_AddExpRecursiveMove(
                MulExp_AddExpRecursiveMove::from_pest(&mut inner)?,
            )),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// * 乘除模表达式 MulExp -> UnaryExp | MulExp ('*' | '/' | '%') UnaryExp
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::MulExpRecursiveMove))]
pub struct MulExpRecursiveMove {
    pub exponent_operator: ExponentOperator,
    pub unary_exp: UnaryExp,
    pub mul_exp_recursive_move: Box<Option<MulExpRecursiveMove>>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for MulExpRecursiveMove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.mul_exp_recursive_move.as_ref() {
            Some(this) => write!(f, "{} {} {}", self.exponent_operator, self.unary_exp, this),
            None => write!(f, "{} {}", self.exponent_operator, self.unary_exp),
        }
    }
}

#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::ExponentOperator_UnaryExp))]
pub struct ExponentOperator_UnaryExp {
    pub exponent_operator: ExponentOperator,
    pub unary_exp: UnaryExp,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for ExponentOperator_UnaryExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {}", self.exponent_operator, self.unary_exp)
    }
}

#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::UnaryExp_MulExpRecursiveMove))]
pub struct UnaryExp_MulExpRecursiveMove {
    pub unary_exp: UnaryExp,
    pub mul_exp_recursive_move: Option<MulExpRecursiveMove>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for UnaryExp_MulExpRecursiveMove {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.mul_exp_recursive_move.as_ref() {
            Some(this) => write!(f, "{} {}", self.unary_exp, this),
            None => write!(f, "{}", self.unary_exp),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum MulExp {
    ExponentOperator_UnaryExp(ExponentOperator_UnaryExp),
    UnaryExp_MulExpRecursiveMove(UnaryExp_MulExpRecursiveMove),
}

impl fmt::Display for MulExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            MulExp::ExponentOperator_UnaryExp(this) => write!(f, "{this}"),
            MulExp::UnaryExp_MulExpRecursiveMove(this) => write!(f, "{this}"),
        }
    }
}

impl<'pest> FromPest<'pest> for MulExp {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 MulExp");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in MulExp:\n{pair:#?}");
        let Rule::MulExp = pair.as_rule() else {
            trace!("\n{:#?} != Rule::MulExp", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::ExponentOperator_UnaryExp => Ok(MulExp::ExponentOperator_UnaryExp(
                ExponentOperator_UnaryExp::from_pest(&mut inner)?,
            )),
            Rule::UnaryExp_MulExpRecursiveMove => Ok(MulExp::UnaryExp_MulExpRecursiveMove(
                UnaryExp_MulExpRecursiveMove::from_pest(&mut inner)?,
            )),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}

// * 一元表达式 UnaryExp -> UnaryOp UnaryExp | Ident'(' [FuncRParams] ')' | PrimaryExp

// UnaryOperator_UnaryExp = { UnaryOperator ~ UnaryExp }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::UnaryOperator_UnaryExp))]
pub struct UnaryOperator_UnaryExp {
    pub unary_operator: UnaryOperator,
    pub unary_exp: Box<UnaryExp>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for UnaryOperator_UnaryExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}{}", self.unary_operator, self.unary_exp)
    }
}

// Ident_FuncRParams = { Ident ~ "(" ~ FuncRParams? ~ ")" }
#[derive(Debug, PartialEq, FromPest)]
#[pest_ast(rule(Rule::Ident_FuncRParams))]
pub struct Ident_FuncRParams {
    pub ident: Ident,
    pub func_r_params: Box<Option<FuncRParams>>,
    #[pest_ast(outer(with(get_meta_info)))]
    pub meta_info: MetaInfo,
}

impl fmt::Display for Ident_FuncRParams {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.func_r_params.as_ref() {
            Some(this) => write!(f, "{}({})", self.ident, this),
            None => write!(f, "{}()", self.ident),
        }
    }
}

// UnaryExp = {
//     UnaryOperator_UnaryExp
//   | Ident_FuncRParams
//   | PrimaryExp
// }
#[derive(Debug, PartialEq)]
pub enum UnaryExp {
    UnaryOperator_UnaryExp(UnaryOperator_UnaryExp),
    Ident_FuncRParams(Ident_FuncRParams),
    PrimaryExp(PrimaryExp),
}

impl fmt::Display for UnaryExp {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            UnaryExp::UnaryOperator_UnaryExp(this) => write!(f, "{this}"),
            UnaryExp::Ident_FuncRParams(this) => write!(f, "{this}"),
            UnaryExp::PrimaryExp(this) => write!(f, "{this}"),
        }
    }
}

impl<'pest> FromPest<'pest> for UnaryExp {
    type Rule = Rule;
    type FatalError = from_pest::Void;
    #[tracing::instrument(skip_all)]
    fn from_pest(
        pest: &mut iterators::Pairs<'pest, Rule>,
    ) -> Result<Self, from_pest::ConversionError<from_pest::Void>> {
        trace!("正在解析 UnaryExp");
        let pair = pest.next().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("pair in UnaryExp:\n{pair:#?}");
        let Rule::UnaryExp = pair.as_rule() else {
            trace!("\n{:#?} != Rule::UnaryExp", pair.as_rule());
            return Err(from_pest::ConversionError::NoMatch);
        };

        let mut inner = pair.into_inner();
        trace!("inner:\n{inner:#?}");
        let inner_pair = inner.peek().ok_or(from_pest::ConversionError::NoMatch)?;
        trace!("inner_pair:\n{inner_pair:#?}");
        match inner_pair.as_rule() {
            Rule::UnaryOperator_UnaryExp => Ok(UnaryExp::UnaryOperator_UnaryExp(
                UnaryOperator_UnaryExp::from_pest(&mut inner)?,
            )),
            Rule::Ident_FuncRParams => Ok(UnaryExp::Ident_FuncRParams(
                Ident_FuncRParams::from_pest(&mut inner)?,
            )),
            Rule::PrimaryExp => Ok(UnaryExp::PrimaryExp(PrimaryExp::from_pest(&mut inner)?)),
            _ => Err(from_pest::ConversionError::NoMatch),
        }
    }
}
